﻿using LightCAD.MathLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace LightCAD.Core.Elements
{
    /// <summary>
    /// 直接组件元素，没有实例化，例如墙、管道、风管、线缆、桥架等等
    /// </summary>
    public class DirectComponent : LcElement, IComponentInstance
    {
        public LcComponentDefinition Definition { get; }
        /// <summary>
        /// 基准线，例如墙体拖拽编辑的线
        /// </summary>
        public Curve2d BaseCurve { get; set; }
        public Transform3d Transform3d { get; private set; }

        public Solid3dCollection Solids { get; }
        /// <summary>
        /// 外形线，例如墙的外形
        /// </summary>
        public virtual Curve2dGroupCollection Curves { get; set; }
        public List<Vector2> Points { get; set; } = new List<Vector2>();
        public LcParameterSet Parameters { get; set; }
        public DirectComponent(LcComponentDefinition cptDef)
        {
            this.Definition = cptDef;
            this.Type = BuiltinElementType.DirectComponent;
            this.Parameters = new LcParameterSet(cptDef.Parameters);
        }

        public override Curve2d[] GetCurves()
        {
            var curves = new List<Curve2d>();

            curves.AddRange(this.Curves.SelectMany(cg=>cg.Curve2ds.Select(c=>c.Clone())));

            return curves.ToArray();
        }
        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var dirCom = src as DirectComponent;
            this.BaseCurve = dirCom.BaseCurve.Clone();
            this.Curves.Clear();
            var grps = dirCom.Curves.Select(grp => grp.Clone()).ToList();
            this.Curves.AddRange(grps);
            this.Points = dirCom.Points.Clone();
            this.Parameters = dirCom.Parameters.Clone();

        }
        public override LcElement Clone()
        {
            var newEle = new DirectComponent(this.Definition);
            newEle.Copy(this);
            return newEle;
        }

        public List<AssociatePoint> AssociatePoints { get; set; } = new List<AssociatePoint>();

        public List<AssociateElement> AssociateElements { get; set; } = new List<AssociateElement>();

        public int MaterialId { get; set; } = 0;
        public string MaterialName { get; set; }
        public LcMaterial Material { get; set; }

        private object _action;
        public object Rt3DAction
        {
            get
            {
                if (_action == null)
                {
                    LcDocument.Element3dActions.TryGetValue(this.Type, out _action);
                }
                if (_action == null)
                {
                    throw new Exception($"Can't find Action. (ElementType={this.Type})");
                }
                return _action;
            }
        }

        private ListEx<Vector3> snap3dPoints = null;
        public ListEx<Vector3> Snap3dPoints
        {
            get
            {
                if (snap3dPoints == null)
                    snap3dPoints = new ListEx<Vector3>();
                return snap3dPoints;
            }
        }

        public virtual void Invoke(string methodName, InvokeToken token)
        {
        }
        public override Box2 GetBoundingBox()
        {
            var line2d = (BaseCurve as Line2d);
            return new Box2().SetFromPoints(line2d.Start, line2d.End);
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            var line2d = (BaseCurve as Line2d);
            return new Box2().SetFromPoints(matrix.MultiplyPoint(line2d.Start), matrix.MultiplyPoint(line2d.End));
        }

        public override void Translate(double dx, double dy)
        {
            //var line2d = this.BaseCurve as Line2d;
            //var ts = new Vector2d(line2d.Start.X + dx, line2d.Start.Y + dy);
            //var te = new Vector2d(line2d.End.X + dx, line2d.End.Y + dy);
            //Set(ts, te);
            //ResetBoundingBox();
            //ResetShapeCurves();

        }


        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteBaseProperties(writer, soptions);
            base.WriteProperties(writer, soptions);

            if (BaseCurve != null)
            {
                writer.WritePropertyName(nameof(BaseCurve));
                writer.WriteCurve2dObject(this.BaseCurve, soptions);
            }
            //writer.WriteCollectionProperty<Curve2d>(nameof(Curves), this.Curves, soptions,(w,c)=>w.WriteCurve2dObject(c,soptions));
            writer.WriteCollectionProperty<Vector2>(nameof(Points), this.Points, soptions);
            //writer.WriteStringProperty("TypeName", this.Type.Name);

            if (this.Definition != null)
            {
                writer.WriteStringProperty("ComponentId", this.Definition.Uuid);
            }

            writer.WriteCollectionProperty("ParameterValues", this.Parameters.Values, soptions, (w, val) =>
            {
                writer.WriteStartObject();
                writer.WriteStringProperty("Type", val?.GetType()?.Name ?? "Null");
                writer.WritePropertyName("Value");
                if (val is List<Curve2d> curves)
                {
                    writer.WriteStartArray();
                    for (int i = 0; i < curves.Count; i++)
                    {
                        var curve = curves[i];
                        writer.WriteStartObject();
                        writer.WriteNumberProperty(nameof(curve.Type), (int)curve.Type);
                        Curve2dUtils.WriteProperties(writer, curves[i], soptions);
                        writer.WriteEndObject();
                    }
                    writer.WriteEndArray();
                }
                else if (val is Vector3 vec)
                {
                    writer.WriteStartArray();
                    writer.WriteNumberValue(vec.X);
                    writer.WriteNumberValue(vec.Y);
                    writer.WriteNumberValue(vec.Z);
                    writer.WriteEndArray();
                }
                else
                {
                    JsonSerializer.Serialize(writer, val, soptions);
                }
                writer.WriteEndObject();
            });
        }
        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);
            if (jele.TryGetProperty(nameof(BaseCurve), out _))
            {
                this.BaseCurve = jele.ReadCurve2dProperty(nameof(BaseCurve));
            }
           //this.Curves=  jele.ReadCurve2dListProperty(nameof(Curves));
            this.Points=  (List<Vector2>)jele.ReadListProperty<Vector2>(nameof(Points));

            var exist = jele.TryGetProperty("ParameterValues", out JsonElement prop);
            if (exist && !prop.NullOrUndefined())
            {
                var arr = prop.EnumerateArray().ToArray();
                for (var i = 0; i < arr.Length; i++)
                {
                    var item = arr[i];
                    string type = item.ReadStringProperty("Type");
                    object obj = null;
                    if (type == "List<Curve2d>")
                    {
                        obj = item.ReadCurve2dListProperty("Value");
                    }
                    else if (type == "Vector3")
                    {
                        obj = item.ReadVector3dProperty("Value");
                    }
                    else
                    {
                        if (type == "Double")
                        {
                            obj = item.ReadObjectProperty<double>("Value");
                        }
                        else if (type == "String")
                        {
                            obj = item.ReadObjectProperty<string>("Value");
                        }
                    }

                    this.Parameters.SetValue(i, obj);
                }
            }
        }

        public virtual void ResetCache()
        {
            this.ResetBoundingBox();
        }
    }
}
